use core::{ffi::c_void, ptr::null_mut};

use crate::{
    c::rtx_os::{
        osFlagsWaitAll, osFlagsWaitAny, osPriority_t_osPriorityHigh, osPriority_t_osPriorityHigh1, osPriority_t_osPriorityHigh2, osRtxThread_t, osThreadAttr_t, osThreadDetached, osThreadFlagsClear, osThreadFlagsSet, osThreadFlagsWait, osThreadId_t, osThreadJoin, osThreadJoinable, osThreadNew
    },
    error::{parse_error, parse_flags_error, Error},
    utils::AlignStore,
};

const CB_SIZE: usize = size_of::<osRtxThread_t>();

pub const PRIORITY_HIGH: i32 = osPriority_t_osPriorityHigh;
pub const PRIORITY_HIGH1: i32 = osPriority_t_osPriorityHigh1;
pub const PRIORITY_HIGH2: i32 = osPriority_t_osPriorityHigh2;

pub const THREAD_DETACHED: u32 = osThreadDetached;
pub const THREAD_JOINABLE: u32 = osThreadJoinable;

pub struct Thread<const STACK_SIZE: usize, const PRIORITY: i32, const ATTR: u32> {
    cb_mem: AlignStore<CB_SIZE>,
    stack_mem: AlignStore<STACK_SIZE>,
    id: osThreadId_t,
}

impl<const STACK_SIZE: usize, const PRIORITY: i32, const ATTR: u32>
    Thread<STACK_SIZE, PRIORITY, ATTR>
{
    pub const fn default() -> Self {
        Self {
            cb_mem: AlignStore::new(),
            stack_mem: AlignStore::new(),
            id: null_mut(),
        }
    }

    pub unsafe fn unsafe_new(
        &mut self,
        name: &str,
        func: extern "C" fn(_: *mut c_void),
        arg: *mut c_void,
    ) {
        let attr = osThreadAttr_t {
            name: name.as_ptr() as *const i8,
            attr_bits: ATTR,
            cb_mem: self.cb_mem.raw_ptr(),
            cb_size: CB_SIZE as u32,
            stack_mem: self.stack_mem.raw_ptr(),
            stack_size: STACK_SIZE as u32,
            priority: PRIORITY,
            tz_module: 0,
            reserved: 0,
        };
        self.id = osThreadNew(Some(func), arg, &attr);
        assert!(self.id != null_mut(), "thread create fail");
    }

    pub fn set_flags(&self, flags: u32) -> Result<(), Error> {
        let ret = unsafe { osThreadFlagsSet(self.id, flags) };
        parse_flags_error(ret)
    }

    pub fn join(&self) -> Result<(), Error> {
        let ret = unsafe { osThreadJoin(self.id) };
        parse_error((), ret)
    }

}

pub fn clear_falgs(flags: u32) -> Result<(), Error> {
    let ret = unsafe { osThreadFlagsClear(flags) };
    parse_flags_error(ret)
}

pub fn wait_any_flags(flags: u32, timeout: u32) -> Result<(), Error> {
    let ret = unsafe { osThreadFlagsWait(flags, osFlagsWaitAny, timeout) };
    parse_flags_error(ret)
}

pub fn wait_all_flags(flags: u32, timeout: u32) -> Result<(), Error> {
    let ret = unsafe { osThreadFlagsWait(flags, osFlagsWaitAll, timeout) };
    parse_flags_error(ret)
}
